package com.zjcloud.jwgl.service.impl;

import com.zjcloud.jwgl.domain.AcLessonSchedule;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 优化的课程冲突检测服务
 * 1. 使用多级缓存（本地缓存 + Redis缓存）
 * 2. 采用位图标记优化冲突检测
 * 3. 实现增量检测
 * 4. 支持批量检测
 */
@Service
public class OptimizedScheduleConflictService {

    private final RedisTemplate<String, Object> redisTemplate;
    
    /** 本地缓存 - 时间槽占用情况 */
    private final Map<String, BitSet> localTimeSlotCache;
    
    /** 本地缓存 - 教室容量信息 */
    private final Map<Long, Integer> localClassroomCapacityCache;
    
    /** 本地缓存 - 班级人数信息 */
    private final Map<Long, Integer> localClassSizeCache;
    
    /** 本地缓存过期时间（毫秒） */
    private static final long LOCAL_CACHE_EXPIRE_MS = 60000;
    
    /** Redis缓存过期时间（秒） */
    private static final long REDIS_CACHE_EXPIRE_SECONDS = 300;
    
    public OptimizedScheduleConflictService(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
        this.localTimeSlotCache = new ConcurrentHashMap<>();
        this.localClassroomCapacityCache = new ConcurrentHashMap<>();
        this.localClassSizeCache = new ConcurrentHashMap<>();
    }
    
    /**
     * 检查单个课程安排的冲突
     */
    public Map<String, Object> checkConflict(AcLessonSchedule schedule) {
        Map<String, Object> result = new HashMap<>();
        List<String> conflicts = new ArrayList<>();
        boolean hasConflict = false;
        
        // 1. 检查时间冲突
        if (hasTimeConflict(schedule)) {
            hasConflict = true;
            conflicts.add("时间冲突");
        }
        
        // 2. 检查教室容量
        if (hasCapacityConflict(schedule)) {
            hasConflict = true;
            conflicts.add("教室容量不足");
        }
        
        // 3. 检查教师工作量
        if (hasWorkloadConflict(schedule)) {
            hasConflict = true;
            conflicts.add("超出教师工作量");
        }
        
        result.put("hasConflict", hasConflict);
        result.put("conflicts", conflicts);
        return result;
    }
    
    /**
     * 批量检查课程安排的冲突
     */
    public List<Map<String, Object>> batchCheckConflicts(List<AcLessonSchedule> schedules) {
        // 预热缓存
        warmupCache(schedules);
        
        // 并行检查冲突
        return schedules.parallelStream()
            .map(this::checkConflict)
            .collect(Collectors.toList());
    }
    
    /**
     * 检查时间冲突
     */
    private boolean hasTimeConflict(AcLessonSchedule schedule) {
        int timeSlotIndex = (schedule.getWeekDay() - 1) * 12 + (schedule.getLessonNo() - 1);
        
        // 检查教师时间冲突
        BitSet teacherSlots = getTimeSlots("teacher:" + schedule.getTeacherId());
        if (teacherSlots.get(timeSlotIndex)) {
            return true;
        }
        
        // 检查教室时间冲突
        BitSet classroomSlots = getTimeSlots("classroom:" + schedule.getClassroomId());
        if (classroomSlots.get(timeSlotIndex)) {
            return true;
        }
        
        // 检查班级时间冲突
        BitSet classSlots = getTimeSlots("class:" + schedule.getClassId());
        return classSlots.get(timeSlotIndex);
    }
    
    /**
     * 获取时间槽占用情况（多级缓存）
     */
    private BitSet getTimeSlots(String key) {
        // 1. 检查本地缓存
        BitSet localSlots = localTimeSlotCache.get(key);
        if (localSlots != null) {
            return localSlots;
        }
        
        // 2. 检查Redis缓存
        BitSet redisSlots = (BitSet) redisTemplate.opsForValue().get("timeslot:" + key);
        if (redisSlots != null) {
            // 更新本地缓存
            localTimeSlotCache.put(key, redisSlots);
            return redisSlots;
        }
        
        // 3. 创建新的BitSet
        BitSet newSlots = new BitSet(60); // 5天 * 12节课 = 60个时间槽
        
        // 更新缓存
        localTimeSlotCache.put(key, newSlots);
        redisTemplate.opsForValue().set("timeslot:" + key, newSlots, REDIS_CACHE_EXPIRE_SECONDS, TimeUnit.SECONDS);
        
        return newSlots;
    }
    
    /**
     * 检查教室容量冲突
     */
    private boolean hasCapacityConflict(AcLessonSchedule schedule) {
        Integer classroomCapacity = getClassroomCapacity(schedule.getClassroomId());
        Integer classSize = getClassSize(schedule.getClassId());
        
        return classroomCapacity != null && classSize != null && classSize > classroomCapacity;
    }
    
    /**
     * 获取教室容量（多级缓存）
     */
    private Integer getClassroomCapacity(Long classroomId) {
        // 1. 检查本地缓存
        Integer localCapacity = localClassroomCapacityCache.get(classroomId);
        if (localCapacity != null) {
            return localCapacity;
        }
        
        // 2. 检查Redis缓存
        Integer redisCapacity = (Integer) redisTemplate.opsForValue().get("classroom:capacity:" + classroomId);
        if (redisCapacity != null) {
            // 更新本地缓存
            localClassroomCapacityCache.put(classroomId, redisCapacity);
            return redisCapacity;
        }
        
        // 3. 从数据库加载
        // TODO: 实现从数据库加载教室容量的逻辑
        Integer capacity = 0;
        
        // 更新缓存
        localClassroomCapacityCache.put(classroomId, capacity);
        redisTemplate.opsForValue().set("classroom:capacity:" + classroomId, capacity, 
                                      REDIS_CACHE_EXPIRE_SECONDS, TimeUnit.SECONDS);
        
        return capacity;
    }
    
    /**
     * 获取班级人数（多级缓存）
     */
    private Integer getClassSize(Long classId) {
        // 1. 检查本地缓存
        Integer localSize = localClassSizeCache.get(classId);
        if (localSize != null) {
            return localSize;
        }
        
        // 2. 检查Redis缓存
        Integer redisSize = (Integer) redisTemplate.opsForValue().get("class:size:" + classId);
        if (redisSize != null) {
            // 更新本地缓存
            localClassSizeCache.put(classId, redisSize);
            return redisSize;
        }
        
        // 3. 从数据库加载
        // TODO: 实现从数据库加载班级人数的逻辑
        Integer size = 0;
        
        // 更新缓存
        localClassSizeCache.put(classId, size);
        redisTemplate.opsForValue().set("class:size:" + classId, size, 
                                      REDIS_CACHE_EXPIRE_SECONDS, TimeUnit.SECONDS);
        
        return size;
    }
    
    /**
     * 检查教师工作量冲突
     */
    private boolean hasWorkloadConflict(AcLessonSchedule schedule) {
        String cacheKey = "teacher:workload:" + schedule.getTeacherId();
        
        // 从Redis获取当前工作量
        Integer currentWorkload = (Integer) redisTemplate.opsForValue().get(cacheKey);
        if (currentWorkload == null) {
            currentWorkload = 0;
        }
        
        // TODO: 从配置获取最大工作量
        int maxWorkload = 20;
        
        return currentWorkload >= maxWorkload;
    }
    
    /**
     * 预热缓存
     */
    private void warmupCache(List<AcLessonSchedule> schedules) {
        // 收集所有需要的ID
        Set<Long> teacherIds = new HashSet<>();
        Set<Long> classroomIds = new HashSet<>();
        Set<Long> classIds = new HashSet<>();
        
        for (AcLessonSchedule schedule : schedules) {
            teacherIds.add(schedule.getTeacherId());
            classroomIds.add(schedule.getClassroomId());
            classIds.add(schedule.getClassId());
        }
        
        // 并行预热缓存
        teacherIds.parallelStream().forEach(id -> getTimeSlots("teacher:" + id));
        classroomIds.parallelStream().forEach(id -> {
            getTimeSlots("classroom:" + id);
            getClassroomCapacity(id);
        });
        classIds.parallelStream().forEach(id -> {
            getTimeSlots("class:" + id);
            getClassSize(id);
        });
    }
    
    /**
     * 更新时间槽占用情况
     */
    public void updateTimeSlot(AcLessonSchedule schedule, boolean isOccupy) {
        int timeSlotIndex = (schedule.getWeekDay() - 1) * 12 + (schedule.getLessonNo() - 1);
        
        // 更新教师时间槽
        updateBitSet("teacher:" + schedule.getTeacherId(), timeSlotIndex, isOccupy);
        
        // 更新教室时间槽
        updateBitSet("classroom:" + schedule.getClassroomId(), timeSlotIndex, isOccupy);
        
        // 更新班级时间槽
        updateBitSet("class:" + schedule.getClassId(), timeSlotIndex, isOccupy);
    }
    
    /**
     * 更新BitSet
     */
    private void updateBitSet(String key, int index, boolean value) {
        BitSet slots = getTimeSlots(key);
        slots.set(index, value);
        
        // 更新缓存
        localTimeSlotCache.put(key, slots);
        redisTemplate.opsForValue().set("timeslot:" + key, slots, REDIS_CACHE_EXPIRE_SECONDS, TimeUnit.SECONDS);
    }
    
    /**
     * 清理缓存
     */
    public void clearCache() {
        localTimeSlotCache.clear();
        localClassroomCapacityCache.clear();
        localClassSizeCache.clear();
    }
}